home *** CD-ROM | disk | FTP | other *** search
/ The X-Philes (2nd Revision) / The X-Philes Number 1 (1995).iso / xphiles / hp48_2 / chip-48.src < prev    next >
Text File  |  1995-03-31  |  37KB  |  1,582 lines

  1. Article 1670 of comp.sys.handhelds:
  2. Path: en.ecn.purdue.edu!noose.ecn.purdue.edu!samsung!zaphod.mps.ohio-state.edu!sdd.hp.com!ucsd!ucbvax!bloom-beacon!eru!hagbard!sunic!news.funet.fi!funic!santra!news
  3. From: gson@niksula.hut.fi (Andreas Gustafsson)
  4. Newsgroups: comp.sys.handhelds
  5. Subject: Source code to CHIP-48 version 2.25
  6. Message-ID: <1990Oct6.183603.29151@santra.uucp>
  7. Date: 6 Oct 90 18:36:03 GMT
  8. Sender: news@santra.uucp (Cnews - USENET news system)
  9. Reply-To: gson@niksula.hut.fi (Andreas Gustafsson)
  10. Organization: Helsinki University of Technology, Finland
  11. Lines: 1567
  12.  
  13. Due to popular demand, I'm posting the complete source code for the
  14. CHIP-48 video game interpreter to comp.sys.handhelds.  I hope this
  15. will inspire others to write more free machine code software for the
  16. HP48SX.
  17.  
  18. The intention of this posting is not that people should actually
  19. assemble the code; it's much easier to get the binary which was
  20. posted recently and is also available by FTP from vega.hut.fi as
  21. /pub/misc/hp48sx/asap/chip48-2.25-bin.Z.  Rather, it should 
  22. serve as a source of programming tips for those writing their own
  23. machine code programs.
  24.  
  25. This source is written for the ASAP assembler, version 1.01.  ASAP is
  26. also FTP:able from vega.hut.fi.  To run the assembler, you need a
  27. 32-bit Unix machine and Perl 3.0 which is available from most major
  28. Unix archive sites.
  29.  
  30. Here it is.  Enjoy!
  31. ================================ Cut here ================================
  32. ; @(#) chip.asap 2.25 9/15/90
  33. ;
  34. ; chip.asap -- a CHIP-8 interpreter for the HP48SX
  35. ;
  36. ; (C) Copyright 1990 Andreas Gustafsson
  37. ;
  38. ;     Noncommercial distribution allowed, provided that this
  39. ;     copyright message is preserved, and any modified versions
  40. ;     are clearly marked as such.  
  41. ;
  42. ;     The program makes use of undocumented low-level features of
  43. ;     the HP48SX calculator, and may or may not cause loss of data,
  44. ;     excessive battery drainage, and/or damage to the calculator
  45. ;     hardware.  The Author takes no responsibility whatsoever for 
  46. ;     any damage caused by the use of this program.
  47. ;
  48. ;     THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
  49. ;     IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  50. ;     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  51. ;
  52.  
  53. ;
  54. ; Register usage:
  55. ;
  56. ; d0 = general pointing
  57. ; d1 = points to chip-8 instruction (physical)
  58. ;
  59. ; r0 = CHIP-8 I register
  60. ; r1 = last time value
  61. ; r2 = physical address of virtual zero
  62. ; r3 = CHIP-8 PC
  63. ;
  64.  
  65. ; standard preamble for Kermit download
  66.     data.b    'H'
  67.     data.b    'P'
  68.     data.b    'H'
  69.     data.b    'P'
  70.     data.b    '4'
  71.     data.b    '8'
  72.     data.b    '-'
  73.     data.b    'A'
  74.     data.a    #2dcc        ; machine code object
  75. begin:    data.a    end-begin    ; length of object
  76. ; end of preamble
  77.  
  78.  
  79. ; HP48SX ROM locations
  80. ; These are for the Revision A ROM, they may need to be changed for
  81. ; other revisions.
  82.  
  83.     flush_kbd=#00d57    ; flush keyboard buffer
  84.     do_in_c=#01160        ; perform "in.4 c" instruction
  85.     alloc_str=#05b7d    ; allocate string
  86.     push_r0_shortint=#06537    ; push r0 as short integer, restore regs
  87.     save_rpl_regs=#0679b    ; save d0, d1, b, d
  88.     restore_rpl_regs=#067d2    ; restore d0, d1, b, d
  89.     check_1_arg=#18abf    ; make sure stack isn't empty
  90.  
  91. ; HP48SX RAM locations
  92.  
  93.     crcval=#00104        ; hardware CRC register
  94.     hwtimer=#00138        ; hardware timer
  95.     stackdisp_ptr=#7055B    ; contains address of stack display
  96.     menudisp_ptr=#70551    ; contains address of menu display
  97.     flags_37=#706ce        ; flags -37 to -40
  98.  
  99. ; data area layout (with a smarter assembler these offsets could be
  100. ; calculated automatically).
  101. ; Don't rearrange; in particular, the variables must be first and
  102. ; the order of the  "V10".."V15" pseudovariables is important.
  103.  
  104.     ofs_vars=0        ; CHIP-8 variables, 16 vars * 2 nibbles = 32
  105.     ofs_timer=32        ; delay timer "V10", size = 2
  106.     ofs_sound=34        ; sound timer "V11", size = 2
  107.     ofs_sndon=36        ; sound on/off flag "V12.0", size = 1
  108.     ofs_sdata=37        ; speaker data "V12.1", "V13", size = 3
  109.     ofs_ckeys=40        ; control key status "V14-15", size=4
  110.     ofs_csp=44        ; CHIP-8 stack pointer, size 5
  111.     ofs_cstack=49        ; CHIP-8 stack, size "stacknibbles" = 64
  112.     ofs_linetab=113        ; display row table, size 64 * 5 = 320
  113.     ofs_regsave=433        ; r0..r3 temporary save location, size = 20
  114.     ofs_end=453        ; end of data area
  115.  
  116. ; don't change these without updating the offsets above also
  117.     
  118.     stacklevels=16        ; size of CHIP-8 stack
  119.     stacknibbles=64        ; 4*stacklevels
  120.  
  121. ; execution begins here
  122.  
  123.     call.a    check_1_arg    ; check that stack is not empty
  124.  
  125.     call.a    save_rpl_regs    ; call ROM routine to save d0, d1, b, d
  126.  
  127.     ; if flag -40 (clock display) is set, clear it and exit.
  128.     ; This is because the clock display interrupt (or whatever)
  129.     ; causes problems, and doesn't get turned off just by clearing
  130.     ; the flag.  However, it does get turned off when the screen is 
  131.     ; redrawn after we exit, so the next time this program is started
  132.     ; it will run normally.
  133.     move.p5    flags_37,c
  134.     move.a    c,d0        ; point to flags -37..-40
  135.     move.p    @d0,a        ; get old flags
  136.     brbc    3,a,noclock    ; jump if flag -40 clear
  137.     clrb    3,a        ; clear flag -40
  138.     move.p    a,@d0        ; store updated flags
  139.     jump.3    exit        ; don't continue just yet
  140. noclock:
  141.  
  142.     ; allocate a string for temporary data storage
  143.  
  144.     clrb    #a,st        ; no garbage collection done
  145.     move.p5    ofs_end,c    ; size of uninitialized data area
  146.     call.a    alloc_str    ; call ROM routine to allocate a string
  147.     swap.a    c,d0
  148.     move.a    c,r4        ; now R4 points to the string
  149.  
  150.     ; clear the CHIP-8 variables and initialize some pseudovariables
  151.  
  152.     move.a    c,d0        ; point to variables (uses c value set above)
  153.     clr.w    c
  154.     move.w    c,@d0        ; clear V0..V7
  155.     add.a    16,d0
  156.     move.w    c,@d0        ; clear V8..VF
  157.     add.a    16,d0
  158.     move.p8    #40010000,c    ; set timers to #00, sndon to #1,
  159.                 ; and sdata to #400
  160.     move.8    c,@d0
  161.  
  162.     ; fill "linetab" with pointers to the display rows
  163.  
  164.     move.a    r4,a        ; get start of data area
  165.     move.p5    ofs_linetab,c
  166.     add.a    a,c
  167.     move.a    c,d0        ; d0 points to linetab
  168.  
  169.     move.p5    stackdisp_ptr,c    ; pointer to address of stack display
  170.     move.p2    56,a        ; 56 rows
  171.     call.3    ltfill
  172.     move.p5    menudisp_ptr,c    ; pointer to address of menu display
  173.     move.p2    8,a        ; 8 rows
  174.     call.3    ltfill
  175.  
  176.     ; allocate a 4 kB string for the CHIP-8 virtual memory
  177.  
  178.     clrb    #a,st        ; no garbage collection done
  179.     move.p5    #2000,c        ; 4 kB in nibbles
  180.     call.a    alloc_str    ; call ROM routine to allocate a string
  181.  
  182.     ; now r0 points to the header of the newly allocated string 
  183.     ; object, and d0 points to the data part
  184.  
  185.     swap.a    d0,c
  186.     move.a    c,r2        ; virtual zero in r2
  187.  
  188.     ; hate nondeterministic bugs...
  189.     clr.w    a
  190.     clr.w    b
  191.     clr.w    c
  192.     clr.w    d
  193.  
  194. chipmain:
  195.     ; copy the chip-8 program from the argument string to the virtual
  196.     ; chip-8 memory
  197.  
  198.     move.a    r2,a        ; get virtual zero
  199.     move.p5    #0400,c        ; virtual #0200 bytes
  200.     add.a    a,c
  201.     move.a    c,d0        ; now d0 points to virtual 0200
  202.  
  203.     move.a    @d1,c        ; point to the argument object
  204.     move.a    c,d1        ; (presumably a string)
  205.     move.a    @d1,a        ; get the object type
  206.     move.p5    #02a2c,c    ; string type prefix
  207.     brne.a    c,a,doerror    ; exit if it isn't a string
  208.     add.a    5,d1        ; skip the type
  209.     move.a    @d1,a        ; get the object length
  210.     sub.a    5,a        ; subtract length of length
  211.     move.p5    #01c00,c    ; this is #1000 - #0200 bytes in nibbles
  212.     brgt.a    c,a,nottoolong
  213. doerror:
  214.     jump.3    errexit        ; string will not fit in 4 k
  215. nottoolong:
  216.     add.a    5,d1        ; point to the object itself
  217.     call.3    copynibbles
  218.  
  219.     ; pop the argument string off the stack
  220.     call.a    restore_rpl_regs
  221.     add.a    5,d1
  222.     inc.a    d
  223.     call.a    save_rpl_regs
  224.  
  225.     ; copy the hexadecimal character font to the virtual chip-8 memory,
  226.     ; unpacking it on-the-fly
  227.  
  228.     move.a    r2,c            ; get virtual zero
  229.     move.a    c,d0            ; now d0 points to 0000 virtual
  230.     move.a    pc,a
  231. ref17:    move.p5    hexfont-ref17,c
  232.     add.a    a,c
  233.     move.a    c,d1            ; now d1 points to the hex patterns
  234.     move.p2    hexfontend-hexfont,a    ; length (8 bits are enough)
  235. fontcopylo:
  236.     move.1    @d1,c            ; read a nibble
  237.     sln.a    c            ; shift to high nibble in byte
  238.     add.a    1,d1
  239.     move.2    c,@d0            ; store byte
  240.     add.a    2,d0            
  241.     dec.a    a
  242.     brnz.b    a,fontcopylo
  243.  
  244.     intoff
  245.  
  246. ; jump here when the "restart" key is pressed
  247. restart:
  248.  
  249.     ; initialize PC and I
  250.     clr.a    c
  251.     move.a    c,r0        ; I=0000 in r0
  252.     move.p3    #200,c        ; relies on c being cleared
  253.     move.a    c,r3        ; PC=0200 in r3
  254.  
  255.     ; initialize stack pointer
  256.  
  257.     move.a    r4,a        ; get start of data area
  258.     move.p5    ofs_csp,c
  259.     add.a    a,c
  260.     move.a    c,d0        ; d0 now points to csp
  261.  
  262.     clr.a    c
  263.     move.a    c,@d0        ; csp cleared
  264.  
  265.     call.3    i00e0        ; erase display
  266.  
  267.     ; initialize the time value
  268.  
  269.     clr.a    c        ; must clear all of c for comparison below
  270.     move.p3    hwtimer,c
  271.     move.a    c,d0        ; point to hardware timer
  272.     move.a    @d0,c
  273. retry1:    move.a    c,a
  274.     move.a    @d0,c
  275.     brne.a    c,a,retry1    ; loop until we get same value twice
  276.  
  277.     move.p5    #00F80,a    ; mask away 7 low bits of time value
  278.     and.a    a,c
  279.     move.a    a,r1
  280.  
  281. nextinstr:
  282.     call.3    realtime    ; do real-time chores
  283.     brcc    nocarry
  284. rtcarry:
  285.     brbs    4,a,restart    ; ENTER was pressed
  286.     ; otherwise, the abort key was pressed; make a clean exit
  287.  
  288. exit:
  289.     call.a    restore_rpl_regs
  290.  
  291.     move.a    @d0,a        ; dispatch next RPL instruction
  292.     add.a    5,d0
  293.     jump.a    @a
  294.  
  295. nocarry:
  296.     ; There appears to be some kind of interrupt that 
  297.     ; checks the keyboard status, and that doesn't get
  298.     ; turned off by "intoff".  Not knowing how to turn it off, we
  299.     ; try to live with it by flushing the keyboard buffer ever so
  300.     ; often, and trying to keep the "out" register zeroed most of
  301.     ; the time (so that the keyboard will appear inactive when
  302.     ; the interrupt checks it, even if keys really are being pressed).
  303.  
  304.     intoff            ; just in case someone turned them on again
  305.  
  306.     call.a    flush_kbd    ; flush keyboard buffer (trashes d1 and c)
  307.  
  308. dispatch:
  309.     ; dispatch a CHIP-8 instruction
  310.     move.a    r3,c    ; get cpc
  311.     move.a    c,a    ; keep unincremented value
  312.     add.a    2,a    ; increment cpc by 2 bytes
  313.     move.a    a,r3    ; store incremented cpc
  314.     call.3    virtophy ; unincremented value is in c
  315.     move.a    c,d1    ; store virtual pc in d1
  316.  
  317.     clr.a     c
  318.     add.a    1,d1    ; point to MSN
  319.     move.p    @d1,c    ; get MSN of first byte of CHIP-8 instruction
  320.     sub.a    1,d1    ; back to beginning of instruction
  321.     add.a    c,c
  322.     add.a    c,c    ; now c = nibble * 4
  323.  
  324.     move.a    pc,a
  325. ref6:    add.a    c,a
  326.     move.p5    jumptab-ref6,c
  327.     add.a    a,c    ; now c = jumptab + nibble * 4
  328.     move.a    c,d0
  329.     clr.a    c    ; clear the 5th nibble
  330.     move.4    @d0,c    ; now c = jumptab entry
  331.  
  332.     move.a    pc,a
  333. jtref:    add.a    c,a    ; now a = jump address
  334.  
  335.     move.a     pc,c
  336. retref:    add.a    retloc-retref,c
  337.     push.a    c    ; push return address on stack
  338.     jump.a    a    ; jump to instruction routine
  339.  
  340. retloc:
  341.     brcs    errexit    ; if carry is set, an error has occurred
  342.     jump.3    nextinstr
  343.  
  344. errexit:    
  345.     move.w    r3,c        ; get the current CHIP-8 PC value
  346.     move.w    c,r0        ; move to R0
  347.     call.a    push_r0_shortint ; ROM routine: push short integer from R0
  348.                 ; (this also restores saved d0, d1, b, d)
  349.     call.a    save_rpl_regs    ; save the registers again (redundant?)
  350.     jump.3    exit
  351.  
  352.  
  353. ; ltfill -- fill a part of "linetab"
  354. ltfill:
  355.     move.a    c,d1
  356.     move.a    @d1,c    ; get display address
  357.     add.a    16,c    ; increment past the GROB header (20 nibbles)
  358.     add.a    4,c
  359. ltfill_loop:
  360.     move.a    c,@d0
  361.     add.a    16,c    ; increment to next row (34 nibbles)
  362.     add.a    16,c    
  363.     add.a    2,c
  364.     add.a    5,d0
  365.     dec.b    a
  366.     brnz.b    a,ltfill_loop
  367.     ret
  368.  
  369.  
  370. ; copynibbles -- copy a memory block
  371. ;
  372. ; d1 points to source, d0 to destination, and 
  373. ; a contains the number of nibbles to copy
  374. copynibbles:
  375. copylo:
  376.     brz.a    a,copyend
  377.     move.1    @d1,c
  378.     move.1    c,@d0
  379.     add.a    1,d0
  380.     add.a    1,d1
  381.     dec.a    a
  382.     jump.3    copylo
  383. copyend:
  384.     ret
  385.  
  386. ; realtime -- do various timer-driven real-time processing
  387. ;
  388. ; In: nothing
  389. ; Out: carry set iff real-time keypress detected; key code is in a
  390. ; Uses: all 16 nibbles of a and c; d0
  391. ;       but neither b nor d
  392. ;
  393. realtime:
  394.  
  395.     ; flip the speaker if the sound is on
  396.     call.3    soundpd0    ; point to sound timer
  397.     move.b    @d0,c
  398.     brz.b    c,silent
  399.     add.a    2,d0        ; point to sound on/off flag
  400.     move.p    @d0,c
  401.     brz.p    c,silent
  402.     add.a    1,d0        ; point to speaker data
  403.     move.3    @d0,c
  404.     out.x    c
  405.     not.x    c    ; turn #400 into #800 and vice versa
  406.     move.p3    #c00,a
  407.     and.x    a,c
  408.     move.3    c,@d0
  409. silent:
  410.  
  411.     ; check the hardware timer register to see if it is time for
  412.     ; a 64 Hz realtime clock tick
  413.  
  414.     clr.a    c        ; must clear all of c for comparison below
  415.     move.p3    hwtimer,c
  416.     move.a    c,d0
  417.     move.a    @d0,c
  418. retry2:    move.a    c,a
  419.     move.a    @d0,c
  420.     brne.a    c,a,retry2    ; loop until we get same value twice
  421.  
  422.     move.p5    #00F80,a    ; mask away 7 low bits of time value
  423.     and.a    a,c
  424.  
  425.     move.a    r1,a        ; now a is old value, c is new value
  426.     brne.a    c,a,dotick
  427.     jump.3    notick        ; code at notick depends on c.0 being zero
  428.  
  429. dotick:    ; handle a 64 Hz tick
  430.  
  431.     clr.a    c        ; decrement r1 by #80
  432.     move.p2    #80,c
  433.     sub.a    c,a
  434.     move.p5    #00F80,c    ; mask 
  435.     and.a    c,a
  436.     move.a    a,r1
  437.  
  438.     call.3    timerpd0    ; point to delay timer
  439.     move.b    @d0,c
  440.     brz.b    c,timerzero
  441.     dec.b    c
  442.     move.b    c,@d0
  443. timerzero:
  444.  
  445.     add.a    2,d0        ; point to sound timer
  446.     move.b    @d0,c
  447.     brz.b    c,soundzero
  448.     dec.b    c
  449.     move.b    c,@d0
  450. soundzero:
  451.  
  452.     ; check for various control keys
  453.  
  454.     call.3    ckeyspd0    ; point d0 to key status
  455.  
  456.     move.p3    #010,c        ; row ENTER..backstep
  457.     out.x    c
  458.     call.a    do_in_c
  459.     move.a    c,a        ; save "in" data in a
  460.     clr.a    c
  461.     out.x    c        ; zero "out" port as fast as possible
  462.  
  463.     move.4    @d0,c        ; get previous key status
  464.     move.4    a,@d0        ; save current key status
  465.     not.a    a        ; get keys that are not pressed
  466.     and.a    c,a        ; ..but were..
  467.     retbs    4,a        ; return with carry set if ENTER pressed
  468.     retbs    0,a        ; same for the backstep key
  469.     brbs    3,a,togglesound    ; +/-
  470. noabort:
  471.     move.p1    #1,c    ; set flag to indicate that a tick took place
  472. notick:    
  473.     retclrc        ; return tick flag in c.0
  474.  
  475.  
  476. ; toggle the sound flag (this is jumped to when the +/- key is pressed)
  477. togglesound:
  478.     call.3    sndonpd0
  479.     move.p    @d0,c
  480.     not.a    c
  481.     move.p1    #1,a
  482.     and.a    a,c
  483.     move.p    c,@d0
  484.     jump.3    noabort
  485.     
  486.  
  487. ; nnnc - get NNN field of current instruction to c register
  488. ;
  489. ; In:    d1 pointing to chip-8 instruction
  490. ; Out:    NNN field of instruction in c (5 valid nibbles)
  491. ; Uses: none
  492. nnnc:
  493.     clr.a     c    ; clear nibbles 3..4
  494.     move.1    2,p
  495.     move.p    @d1,c    ; set nibble 2
  496.     move.1    0,p
  497.     add.a    2,d1    ; point to second byte of instruction
  498.     move.b    @d1,c    ; set nibbles 0 and 1
  499.     sub.a    2,d1    ; restore d1
  500.     ret
  501.  
  502.  
  503. ; virtophy -- convert virtual address to physical address
  504. ;
  505. ; In:   virtual address in c
  506. ; Out:  physical address in c
  507. ; Uses: a
  508. virtophy:
  509.     add.a    c,c    ; convert bytes to nibbles
  510.     move.a    r2,a    ; convert virtual to physical
  511.     add.a    a,c
  512.     ret
  513.  
  514.  
  515. ; varpd0 - get pointer to variable to d0
  516. ; In:    d1 points to nibble containing variable number
  517. ; Out:    d0 points to variable
  518. ; Uses: a,c
  519. varpd0:
  520.     clr.a     c
  521.     move.p    @d1,c    ; get nibble with variable number
  522. cvarpd0:
  523.     add.a    c,c    ; convert bytes to nibbles
  524.     move.a    r4,a
  525.     add.a    a,c
  526.     move.a    c,d0
  527.     ret
  528.  
  529.  
  530. ; var0pd0 -- load d0 with pointer to V0
  531. ;
  532. ; In:    none
  533. ; Out:    d0 points to variable 0
  534. ; Uses: a,c
  535. var0pd0:
  536.     clr.a     c
  537.     move.p1    #0,c
  538.     jump.3    cvarpd0
  539.  
  540.  
  541. ; varfpd0 -- load d0 with pointer to VF
  542. ;
  543. ; In:    none
  544. ; Out:    d0 points to variable F
  545. ; Uses: a,c
  546. varfpd0:
  547.     clr.a     c
  548.     move.p1    #f,c
  549.     jump.3    cvarpd0
  550.  
  551. timerpd0:
  552.     clr.a    c
  553.     move.p2    #10,c
  554.     jump.3    cvarpd0    ; point d0 to timer
  555.  
  556. soundpd0:
  557.     clr.a    c
  558.     move.p2    #11,c
  559.     jump.3    cvarpd0    ; point d0 to sound timer
  560.  
  561. sndonpd0:
  562.     clr.a    c
  563.     move.p2    #12,c
  564.     jump.3    cvarpd0    ; point d0 to sound on/off flag
  565.  
  566. ckeyspd0:
  567.     clr.a    c
  568.     move.p2    #14,c
  569.     jump.3    cvarpd0    ; point d0 to control key status
  570.  
  571.  
  572. ; varxcvarya -- get values of X and Y variables
  573. ; In:    d1 points to instruction
  574. ; Out:    c contains VX, zero padded to .a field
  575. ;    a contains VY, zero padded to .a field
  576. ; Uses:    d0
  577. varxcvarya:
  578.     call.3    varpd0    ; get pointer to X
  579.     clr.a    c
  580.     move.b    @d0,c    ; get X value
  581.     push.a    c
  582.     add.a    3,d1    ; point to Y nibble in instruction
  583.     call.3    varpd0    ; get pointer to Y
  584.     clr.a    a
  585.     move.b    @d0,a    ; get Y value
  586.     pop.a    c
  587.     sub.a    3,d1    ; back to beginning of instruction
  588.     ret
  589.  
  590.  
  591. ; In:    d1 points to beginning instruction
  592. ; Out:    c contains VX (5 nibbles valid)
  593. ;    a contains VY (5 nibbles valid)
  594. ;    d0 points to VX
  595. ; Uses: none
  596. alusetup:
  597.     add.a    3,d1    ; point to Y nibble in instruction
  598.     call.3    varpd0
  599.     sub.a    3,d1
  600.     clr.a    c
  601.     move.2    @d0,c    ; VY in c
  602.     push.a    c
  603.     call.3    varpd0    ; d0 is pointer to VX
  604.     clr.a    a
  605.     move.2    @d0,a    ; VX in a
  606.     pop.a    c    ; VY in c
  607.     swap.a    a,c    ; now VX in c and VY in a
  608.     ret
  609.  
  610. savecarry:
  611.     srn.a    c    ; extract the carry byte
  612.     srn.a    c
  613. lsbcarry:
  614.     move.p2    #01,a    ; use low bit only
  615.     and.b    a,c        
  616.     push.a    c
  617.     call.3    varfpd0    ; get a pointer to VF
  618.     pop.a    c
  619.     move.b    c,@d0    ; store the carry byte
  620.     retclrc
  621.  
  622.  
  623. ; testkey -- check whether a given hex key is pressed
  624. ;
  625. ; In: key number in c (5 low nibbles must be valid)
  626. ; Out: low nibble of c is nonzero iff key is pressed
  627. ; Uses: a,d0
  628. testkey:
  629.     add.a    c,c    ; index into keytab
  630.     move.a    pc,a
  631. ref16:    add.a    c,a
  632.     move.p5    keytab-ref16,c
  633.     add.a    a,c
  634.     move.a    c,d0    ; now d0 points to keytab
  635.     clr.a    c
  636.     move.1    @d0,c    ; get "out" data
  637.     out.x    c
  638.     call.a    do_in_c
  639.     move.a    c,a    ; store the input value in a
  640.     clr.a    c
  641.     out.x    c    ; zero "out" port as fast as possible
  642.     add.a    1,d0
  643.     move.1    @d0,c    ; get "in" mask
  644.     and.a    a,c
  645.     ret
  646.  
  647. ; setup subroutine for fx55 or fx65 instruction
  648. ;
  649. ; In: d0 points to VX
  650. ; Out: d0 points to to V0, a points to VX, and d1 points to MI
  651. varcopysetup:
  652.     swap.a    c,d0        ; copy d0 to c
  653.     move.a    c,d0
  654.     push.a    c        ; save pointer to the last variable
  655.     call.3    var0pd0        ; point d0 to first variable (v0)
  656.     move.a    r0,c        ; get I
  657.     call.3    virtophy
  658.     move.a    c,d1        ; point d1 to data at I
  659.     pop.a    c
  660.     move.a    c,a        ; now a contains pointer to last var.
  661.     ret
  662.  
  663.  
  664. i0:    ; mcode call 
  665.     call.3    nnnc
  666.     move.a    c,a    ; routine address is now in a
  667.     clr.a    c
  668.     move.p2    #e0,c
  669.     breq.a    c,a,i00e0
  670.     move.p2    #ee,c
  671.     breq.a    c,a,i00ee
  672.     retsetc        ; illegal mcode call
  673.  
  674. i00e0:    ; erase screen
  675.     move.a    r4,a        ; get start of data area
  676.     move.p5    ofs_linetab,c
  677.     add.a    c,a
  678.  
  679.     ; a contains linetab pointer
  680.     ; b counts down from 64
  681.     move.p2    64,c
  682.     move.a    c,b
  683. eraselo:
  684.     move.a    a,d0
  685.     move.a    @d0,c
  686.     move.a    c,d0    ; d0 now points to display memory
  687.     clr.w    c
  688.     move.w    c,@d0    ; erase 16 nibbles
  689.     add.a    16,d0
  690.     move.w    c,@d0    ; and 16 more
  691.     add.a    16,d0
  692.     move.b    c,@d0    ; and 2 more, total 34
  693.     add.a    5,a
  694.     dec.b    b
  695.     brnz.b    b,eraselo
  696.     retclrc
  697.  
  698.         
  699. i00ee:    ; subroutine return
  700.     move.a    r4,a        ; get start of data area
  701.     move.p5    ofs_csp,c
  702.     add.a    a,c
  703.     move.a    c,d0    ; c and d0 both point to csp
  704.     move.a    @d0,a    ; now a contains the chip-8 stack pointer (0..4n-4)
  705.     retz.a    a    ; return with carry set if stack underflow
  706.     sub.a    4,a    ; drop one level
  707.     move.a    a,@d0    ; save new csp
  708.     add.a    5,c    ; point c at stack[0]
  709.     add.a    a,c    ; point c at popped level
  710.     move.a    c,d0    ; point d0 at popped level
  711.     clr.a    c
  712.     move.4    @d0,c
  713.     move.a    c,r3    ; set pc
  714.     retclrc
  715.  
  716. i1:    ; 1NNN, jump
  717. dojmp:    call.3    nnnc
  718.     move.a    c,r3    ; assign to pc
  719.     retclrc
  720.  
  721. i2:    ; 2NNN, subroutine call
  722.     move.a    r4,a    ; get start of data area
  723.     move.p5    ofs_csp,c
  724.     add.a    a,c
  725.  
  726.     move.a    c,d0    ; d0 and c both point to csp
  727.     move.a    @d0,a    ; now a contains the chip-8 stack pointer (0..4n-4)
  728.             ; c still points at csp
  729.     add.a    5,c    ; point c at stack[0]
  730.     add.a    a,c    ; point c at first free stack level
  731.     swap.a    c,d0    ; now d0 points to free stack and c points to csp
  732.     move.a    r3,a    ; get pc
  733.     move.4    a,@d0    ; store pc in stack
  734.     swap.a    c,d0    ; now d0 points to cpc again
  735.     move.a    @d0,a    ; now a contains the chip-8 stack pointer (0..4n-4)
  736.     add.a    4,a
  737.     move.p5    stacknibbles,c
  738.     retlt.a    c,a    ; return with carry set if stack overflow
  739.     move.a    a,@d0    ; store incremented sp
  740.     jump.3    dojmp    ; the reset is like 1nnn
  741.  
  742.  
  743. i3:    ; 3XKK, skip if X==KK
  744.     call.3    varpd0    ; get pointer to X
  745.     add.a    2,d1    ; point to second byte of instruction
  746.     move.b    @d1,a    ; now a = KK
  747.     move.b    @d0,c    ; now c = VX
  748. skipeq:
  749.     brne.b    c,a,noskip
  750. doskip:    swap.a    c,r3    ; increment cpc by 2
  751.     add.a    2,c
  752.     swap.a    c,r3
  753. noskip:
  754.     retclrc
  755.  
  756. i4:    ; 4XKK, skip if X<>KK
  757.     call.3    varpd0    ; get pointer to X
  758.     add.a    2,d1    ; point to second byte of instruction
  759.     move.b    @d1,a    ; now a = KK
  760.     move.b    @d0,c    ; now c = VX
  761. skipne:
  762.     breq.b    c,a,noskip
  763.     jump.3    doskip
  764.  
  765. i5:    ; 5XY0, skip if X==Y
  766.     call.3    varxcvarya    ; get VX to c, VY to a
  767.     jump.3    skipeq
  768.  
  769. i9:    ; 9XY0, skip if X!=Y
  770.     call.3    varxcvarya    ; get VX to c, VY to a
  771.     jump.3    skipne
  772.  
  773. i6:    ; 6XKK, load variable by constant
  774.     call.3    varpd0    ; get pointer to X
  775.     add.a    2,d1    ; point to second byte of instruction
  776.     move.b    @d1,a    ; now a = KK
  777.     move.b    a,@d0    ; store in variable
  778.     retclrc
  779.  
  780. i7:    ; 7XKK, add constant to variable
  781.     call.3    varpd0    ; get pointer to X
  782.     add.a    2,d1    ; point to second byte of instruction
  783.     move.b    @d1,a    ; now a = KK
  784.     move.b    @d0,c    ; get old value
  785.     add.b    a,c    ; add KK
  786.     move.b    c,@d0    ; store new value
  787.     retclrc
  788.  
  789.  
  790. i8:    ; arithmetic and logic operations
  791.     add.a    2,d1    ; point to last nibble of instruction
  792.     move.1    @d1,a
  793.     sub.a    2,d1
  794.  
  795.     move.p1    #0,c
  796.     brne.p    c,a,noti8xy0
  797. i8xy0:    ; VX := VY
  798.     call.3    alusetup
  799.     move.b    a,@d0    ; store result
  800.     retclrc
  801. noti8xy0:
  802.  
  803.     move.p1    #1,c
  804.     brne.p    c,a,noti8xy1
  805. i8xy1:    ; VX := VX or VY
  806.     call.3    alusetup
  807.     or.b    a,c
  808.     move.2    c,@d0    ; store result
  809.     retclrc
  810. noti8xy1:
  811.  
  812.     move.p1    #2,c
  813.     brne.p    c,a,noti8xy2
  814. i8xy2:    ; VX := VX and VY
  815.     call.3    alusetup
  816.     and.b    a,c
  817.     move.2    c,@d0    ; store result
  818.     retclrc
  819. noti8xy2:
  820.  
  821.     move.p1    #3,c
  822.     brne.p    c,a,noti8xy3
  823. i8xy3:    ; VX := VX xor VY
  824.     call.3    alusetup
  825.     move.b    a,b
  826.     or.b    c,b    ; (x or y) in b
  827.     and.b    a,c    ; (x and y) in c
  828.     not.b    c    
  829.     and.b    b,c    ; (x xor y) in c
  830.     move.b    c,@d0    ; store result
  831.     retclrc
  832. noti8xy3:
  833.  
  834.     move.p1    #4,c
  835.     brne.p    c,a,noti8xy4
  836. i8xy4:    ; VX := VX + VY; carry in VF
  837.     call.3    alusetup
  838.     add.a    a,c
  839.     move.2    c,@d0
  840.     jump.3    savecarry
  841. noti8xy4:
  842.  
  843.     move.p1    #5,c
  844.     brne.p    c,a,noti8xy5
  845. i8xy5:    ; VX := VX - VY; carry in VF
  846.     call.3    alusetup
  847. subcommon:
  848.     not.b    a    ; do monkey business to get inverted carry
  849.     add.a    a,c
  850.     inc.a    c
  851.     move.2    c,@d0
  852.     jump.3    savecarry
  853. noti8xy5:
  854.  
  855.     move.p1    #6,c
  856.     brne.p    c,a,noti8xy6
  857. i8xy6:    ; VX := VX >> 1; carry in VF
  858.     call.3    alusetup
  859.     move.b    c,a    
  860.     srb.b    c
  861.     move.2    c,@d0    ; store result
  862.     move.b    a,c    ; use low bit of original byte for carry
  863.     jump.3    lsbcarry
  864. noti8xy6:
  865.     move.p1    #7,c
  866.     brne.p    c,a,noti8xy7
  867. i8xy7:    ; VX := VY - VX; carry in VF
  868.     call.3    alusetup
  869.     swap.a    a,c
  870.     jump.3    subcommon
  871.  
  872. noti8xy7:
  873.     move.p1    #e,c
  874.     brne.p    c,a,noti8xye
  875. i8xye:    ; VX := VX << 1; carry in VF
  876.     call.3    alusetup
  877.     add.a    c,c
  878.     move.2    c,@d0
  879.     jump.3    savecarry
  880. noti8xye:
  881.     retsetc
  882.  
  883.  
  884. ia:    ; ANNN, set I
  885.     call.3    nnnc
  886.     move.a    c,r0    ; assign to I
  887.     retclrc
  888.  
  889. ib:    ; parametric jump to NNN+V0
  890.     call.3    varpd0
  891.     call.3    nnnc
  892.     clr.a    a
  893.     move.b    @d0,a
  894.     add.a    a,c
  895.     move.a    c,r3    ; assign to pc
  896.     retclrc
  897.  
  898.  
  899. ic:    ; pseudo-random number
  900.  
  901.     call.3    varpd0        ; now d0 points to VX
  902.     move.p5    crcval,c
  903.     swap.a    c,d0        ; point d0 to hardware crc
  904.     move.2    @d0,a        ; read the low byte of the crc
  905.     swap.a    c,d0        ; now d0 points to VX again
  906.     add.a    2,d1        ; point to second byte of instruction
  907.     move.b    @d1,c        ; get mask
  908.     sub.a    2,d1        ; restore d1
  909.     and.b    a,c        ; mask
  910.     move.b    c,@d0        ; store result
  911.     retclrc
  912.  
  913.  
  914. id_abort:
  915.     jump.3    fx0a_abort
  916.  
  917.  
  918. id:    ; DXYN, show N-byte sprite at MI at screen coordinates (X,Y)
  919.     ;       I doesn't change
  920.  
  921.     ; synchronize with 64 Hz tick
  922. tickwait:
  923.     call.3    realtime
  924.     brcs    id_abort    ; a realtime key was pressed
  925.     brz.p    c,tickwait    ; wait until a tick occus
  926.  
  927.     call.3    save_rregs    ; save r0..r3
  928.  
  929.     move.a    r0,c        ; get I
  930.     call.3    virtophy
  931.     move.a    c,r0        ; now r0 points to sprite
  932.  
  933.     call.3    varxcvarya
  934.     move.a    c,d        ; save X in d
  935.     move.p2    #1f,c
  936.     and.b    c,a        ; mask Y to range 0..31, leave in a
  937.     move.p2    #3f,c
  938.     and.b    c,d        ; mask X to range 0..63, save in d
  939.  
  940.     ; get the number of bytes in the sprite (preserving a and d)
  941.     add.a    2,d1        ; point to N field
  942.  
  943.     clr.b    c
  944.     move.1    @d1,c
  945.     add.b    a,c        ; now c contains Y + sprite length
  946.     move.b    c,b
  947.     move.p2    #20,c
  948.     sub.b    c,b        ; now b contains no. of overshoot lines
  949.     brcc    oshoot
  950.     clr.b    b        ; no overshoot
  951. oshoot:
  952.     clr.b    c        ; get sprite length again
  953.     move.1    @d1,c
  954.     sub.b    b,c        ; subtract overshoot
  955.     move.1    c,0,p        ; now p contains adjusted length
  956.     move.1    p,c,15        ; byte counter in nibble 15 of c
  957.     move.1    14,p
  958.     clr.p    c        ; collision flag in nibble 14 of c
  959.     move.1    0,p
  960.     sub.a    2,d1        ; back to beginning of instruction
  961.  
  962.     call.3    sprite        ; do it
  963.  
  964.     call.3    restore_rregs
  965.  
  966.     call.4    varfpd0        ; d0 points to VF
  967.     clr.b    c
  968.     move.1    14,p
  969.     brz.p    c,nocolls
  970.     inc.b    c
  971. nocolls:
  972.     move.b    c,@d0        ; store collision flag
  973.     move.1    0,p
  974.  
  975.     retclrc    ; id
  976.  
  977.  
  978. ie:    ; skip on key pressed / not pressed
  979.     call.3    varpd0
  980.     clr.a    c
  981.     move.b    @d0,c    ; Telmac key number
  982.     call.3    testkey
  983.     clr.b    d
  984.     move.p    c,d    ; now d.b is nonzero iff key was pressed
  985.     
  986.     add.a    2,d1    ; point to second byte of instruction
  987.     move.b    @d1,a
  988.     sub.a    2,d1
  989.     move.p2    #9e,c
  990.     breq.b    c,a,doex9e
  991.     move.p2    #a1,c
  992.     breq.b    c,a,doexa1
  993.     retsetc
  994.  
  995. doex9e:    move.b    d,c
  996.     clr.b    a
  997.     jump.3    skipne    ; skip iff d!=0 (key was pressed)
  998.  
  999. doexa1:    move.b    d,c
  1000.     clr.b    a
  1001.     jump.3    skipeq    ; skip iff d==0 (key was not pressed)
  1002.  
  1003.  
  1004. ; kwait -- wait for key, return it in low byte of c
  1005. ; In: none
  1006. ; Out: key code in low byte of c
  1007. ; Uses: most everything except d0
  1008. ;
  1009. ; The beep-and-wait-until-released behaviour may seem a bit crummy,
  1010. ; but we're just trying to emulate the original Telmac PROM monitor
  1011. ; keyboard input routine as closely as possible.
  1012. kwait:
  1013.     swap.a    c,d0
  1014.     push.a    c        ; save d0 value
  1015.  
  1016.     clr.a    d        ; low nibble of d used for key code
  1017. kwalo:    move.a    d,c
  1018.     call.3    testkey
  1019.     brnz.p    c,pressed
  1020.     call.3    realtime    ; preserves d
  1021.     brcs    kwabort
  1022.     inc.p    d        ; loop through keys 0..15
  1023.     jump.3    kwalo
  1024.  
  1025. pressed:
  1026.     call.3    soundpd0
  1027.     move.p2    #04,c
  1028.     move.2    c,@d0        ; set sound timer to #04
  1029.     call.3    realtime    ; make some noise
  1030.     brcs    kwabort
  1031.     move.a    d,c
  1032.     call.3    testkey
  1033.     brnz.p    c,pressed    ; wait until key is released
  1034.  
  1035.     pop.a    c        ; restore d0 value
  1036.     move.a    c,d0
  1037.     move.b    d,c
  1038.     retclrc
  1039.  
  1040. kwabort:
  1041.     pop.a    c        ; adjust stack
  1042.     retsetc
  1043.  
  1044. if:    ; misc. functions using VX
  1045.     ; d0 is set up to point to VX
  1046.  
  1047.     call.3    varpd0
  1048.     add.a    2,d1    ; point to second byte of instruction
  1049.     move.b    @d1,a
  1050.     sub.a    2,d1
  1051.  
  1052.     move.p2    #07,c
  1053.     brne.b    c,a,notifx07
  1054. ifx07:    ; read timer
  1055.     swap.a    c,d0
  1056.     push.a    c
  1057.     call.3    timerpd0
  1058.     move.b    @d0,a
  1059.     pop.a    c
  1060.     swap.a    c,d0
  1061.     move.b    a,@d0
  1062.     retclrc
  1063. notifx07:
  1064.  
  1065.     move.p2    #0a,c
  1066.     brne.b    c,a,notifx0a
  1067. ifx0a:    call.3    kwait
  1068.     brcs    fx0a_abort
  1069.     move.b    c,@d0
  1070.     retclrc
  1071. fx0a_abort:
  1072.     pop.a    c    ; adjust stack for forced return
  1073.     jump.3    rtcarry
  1074.  
  1075. notifx0a:
  1076.  
  1077.      move.p2    #15,c
  1078.     brne.b    c,a,notifx15
  1079. ifx15:    ; set timer
  1080.     move.b    @d0,c
  1081.     push.a    c
  1082.     call.3    timerpd0
  1083.     pop.a    c
  1084.     move.b    c,@d0
  1085.     retclrc
  1086. notifx15:
  1087.  
  1088.      move.p2    #18,c
  1089.     brne.b    c,a,notifx18
  1090. ifx18:    ; set sound
  1091.     move.b    @d0,c
  1092.     push.a    c
  1093.     call.3    soundpd0
  1094.     pop.a    c
  1095.     move.b    c,@d0
  1096.     retclrc
  1097. notifx18:
  1098.  
  1099.      move.p2    #1e,c
  1100.     brne.b    c,a,notifx1e
  1101. ifx1e:    ; increment I by VX
  1102.     clr.a    c
  1103.     move.b    @d0,c
  1104.     move.a    r0,a    ; get old I
  1105.     add.x    c,a    ; increment, modifying only 3 low nibbles
  1106.     retcs        ; it wrapped around #1000
  1107.     move.a    a,r0    ; save new I
  1108.     retclrc
  1109. notifx1e:
  1110.  
  1111.      move.p2    #29,c
  1112.     brne.b    c,a,notifx29
  1113. ifx29:    ; point to hex display pattern
  1114.     ; assumes that the hex patterns are at virtual 0000
  1115.     clr.a    c
  1116.     move.1    @d0,c    ; use low nibble of variable
  1117.     move.a    c,a    
  1118.     add.a    c,c    ; * 2
  1119.     add.a    c,c    ; * 4
  1120.     add.a    a,c    ; * 5
  1121.     move.a    c,r0    ; this is the new I
  1122.     retclrc
  1123. notifx29:
  1124.  
  1125.      move.p2    #33,c
  1126.     breq.b    c,a,ifx33
  1127.     jump.3    notifx33
  1128. ifx33:    ; 8-bit binary to decimal conversion
  1129.     move.b    @d0,c
  1130.     move.b    c,d        ; d contains the byte to convert
  1131.  
  1132.     move.a    pc,a
  1133. ref12:    move.p5    dectab-ref12,c
  1134.     add.a    a,c
  1135.     move.a    c,d0        ; d0 points to the conversion table
  1136.  
  1137.     clr.a    a        ; a accumulates decimal result    
  1138. cnvbit:    brz.b    d,cnvend
  1139.     move.b    d,c
  1140.     brbc    0,c,skpbit    ; jump if low-order bit is zero
  1141.     move.3    @d0,c
  1142.     setdec
  1143.     add.a    c,a        ; only 3 low nibbles contain real data
  1144.     sethex
  1145. skpbit:    add.a    3,d0
  1146.     srb.b    d
  1147.     jump.3    cnvbit
  1148.  
  1149. cnvabort:
  1150.     retsetc
  1151.  
  1152. cnvend:
  1153.     move.a    a,b        ; save the decimal value
  1154.     move.a    r0,c        ; get I
  1155.     move.p5    #00ffd,a    ; is I > 0FFD hex?
  1156.     retgt.a    c,a        ; protect memory above 0FFF
  1157.     call.3    virtophy
  1158.     move.a    c,d0        ; virtual I in d0
  1159.     move.a    b,a        ; restore the decimal value
  1160.  
  1161.     clr.a    c
  1162.     move.1    2,p
  1163.     move.p    a,@d0        ; most significant digit first
  1164.     add.a    1,d0
  1165.     move.p    c,@d0        ; zero
  1166.     add.a    1,d0
  1167.     move.1    1,p
  1168.     move.p    a,@d0        ; middle digit
  1169.     add.a    1,d0
  1170.     move.p    c,@d0        ; zero
  1171.     add.a    1,d0
  1172.     move.1    0,p
  1173.     move.p    a,@d0        ; least significant digit
  1174.     add.a    1,d0
  1175.     move.p    c,@d0        ; zero
  1176.     ; I doesn't change
  1177.     retclrc
  1178. notifx33:
  1179.  
  1180.      move.p2    #55,c
  1181.     brne.b    c,a,notifx55
  1182. ifx55:    ; save vars in memory
  1183.     call.3    varcopysetup
  1184. savelo:
  1185.     move.b    @d0,c        ; read a byte from variable
  1186.     move.b    c,@d1        ; store in MI
  1187.     swap.a    c,d0        ; get d0 to c
  1188.     move.a    c,d0
  1189.     brge.a    c,a,doret1    ; are we ready yet?
  1190.     add.a    2,d1
  1191.     add.a    2,d0
  1192.     move.a    r0,c        ; get I value
  1193.     inc.x    c        ; increment 3 low nibbles
  1194.     retcs            ; if carry, we might overwrite #1000
  1195.     move.a    c,r0        ; I changes permanently
  1196.     jump.3    savelo
  1197. doret1:
  1198.     retclrc
  1199.  
  1200. notifx55:
  1201.  
  1202.      move.p2    #65,c
  1203.     brne.b    c,a,notifx65
  1204. ifx65:    ; restore vars from memory
  1205.     call.3    varcopysetup
  1206. restolo:
  1207.     move.b    @d1,c        ; read a byte at MI
  1208.     move.b    c,@d0        ; store in variable
  1209.     swap.a    c,d0        ; get d0 to c
  1210.     move.a    c,d0
  1211.     brge.a    c,a,doret1    ; are we ready yet?
  1212.     add.a    2,d1
  1213.     add.a    2,d0
  1214.     move.a    r0,c        ; get I value
  1215.     inc.x    c        ; increment 3 low nibbles
  1216.     retcs            ; if carry, we wrapped around #1000
  1217.     move.a    c,r0        ; I changes permanently
  1218.     jump.3    restolo
  1219. notifx65:
  1220.     ; unknown FxNN instruction
  1221.     retsetc
  1222.  
  1223.  
  1224. ; save registers r0..r3
  1225. save_rregs:
  1226.     move.a    r4,a        ; get start of data area
  1227.     move.p5    ofs_regsave,c
  1228.     add.a    a,c
  1229.     move.a    c,d0
  1230.  
  1231.     move.a    r0,c
  1232.     move.a    c,@d0
  1233.     add.a    5,d0
  1234.     move.a    r1,c
  1235.     move.a    c,@d0
  1236.     add.a    5,d0
  1237.     move.a    r2,c
  1238.     move.a    c,@d0
  1239.     add.a    5,d0
  1240.     move.a    r3,c
  1241.     move.a    c,@d0
  1242.     add.a    5,d0
  1243.     ret
  1244.  
  1245.  
  1246. ; restore registers r0..r3
  1247. restore_rregs:
  1248.     move.a    r4,a        ; get start of data area
  1249.     move.p5    ofs_regsave,c
  1250.     add.a    a,c
  1251.     move.a    c,d0
  1252.  
  1253.     move.a    @d0,c
  1254.     move.a    c,r0
  1255.     add.a    5,d0
  1256.     move.a    @d0,c
  1257.     move.a    c,r1
  1258.     add.a    5,d0
  1259.     move.a    @d0,c
  1260.     move.a    c,r2
  1261.     add.a    5,d0
  1262.     move.a    @d0,c
  1263.     move.a    c,r3
  1264.     add.a    5,d0
  1265.     ret
  1266.  
  1267.  
  1268. ; sprite: draw a CHIP-8 "sprite", 8 pixels wide by 1..15 pixels high
  1269. ;
  1270. ; in: r0.a  points to sprite data
  1271. ;     a.a   contains x coordinate
  1272. ;     d.a   contains y coordinate
  1273. ;     c.14  zero initial value for collision flag
  1274. ;     c.15  number of lines in sprite
  1275. ; out: c.14 collision flag
  1276. ;
  1277. ; register usage:
  1278. ; a,c used for scratch
  1279. ;  c.15 contains sprite byte (line) count
  1280. ;  c.14 contains collision flag
  1281. ;  c.13 is used as temporary save location for p
  1282. ;  c.12 is nonzero in "short mode" (2x1 pixels)
  1283. ; b contains the byte to display
  1284. ; d contains the 32-bit pixel string
  1285. ; r0 points to sprite data
  1286. ; r1 points to linetab
  1287. ; r2 contains the X offset in nibbles from the beginning of the lcd line
  1288. ; r3 contains entry point to pixel alignment shifts
  1289.  
  1290. sprite:
  1291.     move.1    12,p
  1292.     move.p1    0,c        ; set "short flag"
  1293.     move.1    0,p
  1294.  
  1295.     move.1    12,p
  1296.     brnz.p    c,short1    ; "short mode"?
  1297.     add.a    a,a        ; 2Y (the chip-8 pixels are 2 lines high)
  1298. short1:    move.1    0,p        ; (but not in "short mode"
  1299.  
  1300.     move.a    a,c        ; multiply by 5 to get index into linetab
  1301.     add.a    a,a
  1302.     add.a    a,a
  1303.     add.a    a,c        ; now c contains index to linetab
  1304.  
  1305.     move.a    r4,a        ; get start of data area
  1306.     add.a    c,a
  1307.     move.p5    ofs_linetab,c
  1308.     add.a    a,c
  1309.  
  1310.     move.a    c,r1        ; r1 is the linetab pointer
  1311.  
  1312.     ; calculate X offset in nibbles from the beginning of the lcd line
  1313.     move.a    d,c        ; c = X
  1314.     srb.a    c        ; convert pixels to pixel octets
  1315.     srb.a    c
  1316.     srb.a    c
  1317.     add.a    c,c        ; convert pixel octets to nibbles
  1318.     add.a    c,c
  1319.     move.a    c,r2        ; r2 contains offset from beginning of line
  1320.  
  1321.     ; calculate entry point to alignment shifts
  1322.     move.p2    #07,c        ; d should still contain X
  1323.     and.b    c,d        ; mask out 3 low bits
  1324.     add.a    d,d        ; the shift instructions are 2 nibbles each
  1325.     move.a    pc,a        
  1326. ref11:    move.p5    shifts-ref11,c
  1327.     add.a    a,c
  1328.     add.a    d,c
  1329.     move.a    c,r3        ; r3 now contains entry point to shifts
  1330.  
  1331. linelo:
  1332.     move.a    r0,c        ; get sprite pointer
  1333.     move.a    c,d0
  1334.     move.2    @d0,c        ; byte to display
  1335.     clr.a    b
  1336.     move.b    c,b        ; ..to low byte of b; next byte is clear
  1337.     swap.a    c,d0
  1338.     add.a    2,c        ; advance to next sprite byte
  1339.     move.a    c,r0        ; save sprite pointer
  1340.  
  1341.     ; the low byte of b now contains the sprite byte
  1342.  
  1343.     move.a    r3,c        ; get shift entry point
  1344.     jump.a    c        ; jump to the appropriate shift instruction
  1345.  
  1346.  
  1347. shifts:    add.a    b,b        ; two-nibble instructions
  1348.     add.a    b,b
  1349.     add.a    b,b
  1350.     add.a    b,b
  1351.     add.a    b,b
  1352.     add.a    b,b
  1353.     add.a    b,b
  1354.     add.a    b,b
  1355.  
  1356.     move.a    pc,a
  1357. ref2:    move.p5    pixtab-ref2,c
  1358.     add.a    c,a        ; beginning of pixtab is in a
  1359.  
  1360.     ; keep &pixtab[0] in a at all times
  1361.     clr.a    c
  1362.     move.p    b,c        ; extract a nibble from b
  1363.     add.a    c,c        ; times two
  1364.     add.a    a,c        ; add beginning of pixtab
  1365.     move.a    c,d0        ; point d0 to pixtab byte
  1366.     move.b    @d0,c
  1367.     move.b    c,d        ; now 8 more pixels in d
  1368.  
  1369.     sln.w    d
  1370.     sln.w    d
  1371.     srn.a    b
  1372.  
  1373.     clr.a    c
  1374.     move.p    b,c        ; extract a nibble from b
  1375.     add.a    c,c        ; times two
  1376.     add.a    a,c
  1377.     move.a    c,d0        ; point d0 to pixtab byte
  1378.     move.b    @d0,c
  1379.     move.b    c,d        ; now 8 more pixels in d
  1380.  
  1381.     sln.w    d
  1382.     sln.w    d
  1383.     srn.a    b
  1384.  
  1385.     clr.a    c
  1386.     move.p    b,c        ; extract a nibble from b
  1387.     add.a    c,c        ; times two
  1388.     add.a    a,c
  1389.     move.a    c,d0        ; point d0 to pixtab byte
  1390.     move.b    @d0,c
  1391.     move.b    c,d        ; now 8 more pixels in d
  1392.  
  1393.     sln.w    d
  1394.     sln.w    d
  1395.     srn.a    b
  1396.  
  1397.     clr.a    c
  1398.     move.p    b,c        ; extract a nibble from b
  1399.     add.a    c,c        ; times two
  1400.     add.a    a,c
  1401.     move.a    c,d0        ; point d0 to pixtab byte
  1402.     move.b    @d0,c
  1403.     move.b    c,d        ; now 8 more pixels in d
  1404.  
  1405.     ; now d contains 32 pixels
  1406.  
  1407.     move.a    r1,a        ; get linetab pointer
  1408.     move.a    a,d0        ; ..to d0
  1409.     move.a    @d0,c        ; address of current line to c
  1410.  
  1411.     move.1    12,p
  1412.     brnz.p    c,short2
  1413.     add.a    5,a        ; point 1 line ahead if "short mode",
  1414. short2:    add.a    5,a        ; point 2 lines ahead otherwise
  1415.     move.1    0,p
  1416.  
  1417.     move.a    a,r1        ; this is the new linetab pointer
  1418.  
  1419.     move.a    r2,a        ; X offset in nibbles
  1420.     add.a    a,c        ; add beginning of lcd line
  1421.     move.a    c,d0        ; now d0 points to display buffer
  1422.  
  1423.     ; now we operate on the display buffer with a word length
  1424.     ; that is normally 32 bits, but near the right end we
  1425.     ; must decrease it to avoid wrapping around and messing up
  1426.     ; the left part of the display.  Therefore the word length
  1427.     ; should be min(8, 32-xoff) nibbles.
  1428.  
  1429.     move.p2    32,c
  1430.     sub.b    a,c        ; now c = 32-xoff
  1431.     move.p2    8,a
  1432.     brlt.b    c,a,clip    ; if (32-xoff) is < 8, use that, else 8
  1433.     move.b    a,c
  1434. clip:    dec.b    c        ; .wp field is (p+1) nibbles; compensate
  1435.     move.1    c,0,p        ; set field size
  1436.  
  1437.     move.wp    @d0,c        ; get old display buffer contents
  1438.     move.wp    c,a        ; make a copy in a
  1439.     and.wp    d,c        ; now c = (old and new), a = old, d = new
  1440.     brz.wp    c,nocoll
  1441.  
  1442.     ; there has been a collision; set nibble 14 in c
  1443.     swap.1    p,c,13        ; save p in nibble 13 of c
  1444.     move.1    14,p
  1445.     move.p1    1,c        ; set collision flag in nibble 13 of c
  1446.     swap.1    p,c,13        ; restore p
  1447. nocoll:
  1448.     swap.wp    d,c        ; now d = (old and new), a = old, c = new
  1449.     or.wp    a,c        ; now c = (old or new)
  1450.     not.wp    d
  1451.     and.wp    d,c        ; now c = (old or new) and not(old and new)
  1452.  
  1453.     swap.1    p,c,13
  1454.     move.1    12,p
  1455.     brnz.p    c,short3
  1456.     swap.1    p,c,13
  1457.  
  1458.     move.wp    c,@d0        ; store once
  1459.     add.a    16,d0        ; advance by 34 to next scan line
  1460.     add.a    16,d0
  1461.     add.a    2,d0
  1462.     move.wp    c,@d0        ; store again
  1463.     jump.3    noshort3
  1464. short3:
  1465.     swap.1    p,c,13
  1466.     move.wp    c,@d0        ; store just once
  1467. noshort3:
  1468.     move.1    0,p
  1469.     
  1470.     dec.s    c
  1471.     brz.s    c,lineexit
  1472.     jump.3    linelo
  1473. lineexit:
  1474.  
  1475.     ret  ; sprite
  1476.  
  1477.  
  1478. ; this lookup table serves a dual purpose: it swaps the bits in a nibble
  1479. ; to convert from big-endian to little-endian format, and doubles each bit
  1480. ; to lower the resolution to 64 pixels horizontally.
  1481. pixtab:
  1482.     data.b    !00000000
  1483.     data.b    !11000000
  1484.     data.b    !00110000
  1485.     data.b    !11110000
  1486.     data.b    !00001100
  1487.     data.b    !11001100
  1488.     data.b    !00111100
  1489.     data.b    !11111100
  1490.     data.b    !00000011
  1491.     data.b    !11000011
  1492.     data.b    !00110011
  1493.     data.b    !11110011
  1494.     data.b    !00001111
  1495.     data.b    !11001111
  1496.     data.b    !00111111
  1497.     data.b    !11111111
  1498.  
  1499.  
  1500. ; 4x5 pixel hexadecimal character font patterns
  1501. hexfont:
  1502.     data.5 #F999F
  1503.     data.5 #72262
  1504.     data.5 #F8F1F
  1505.     data.5 #F1F1F
  1506.     data.5 #11F99
  1507.     data.5 #F1F8F
  1508.     data.5 #F9F8F
  1509.     data.5 #4421F
  1510.     data.5 #F9F9F
  1511.     data.5 #F1F9F
  1512.     data.5 #99F9F
  1513.     data.5 #E9E9E
  1514.     data.5 #F888F
  1515.     data.5 #E999E
  1516.     data.5 #F8F8F
  1517.     data.5 #88F8F
  1518. hexfontend:
  1519.  
  1520. ; powers of two in BCD, for binary-to-decimal conversion
  1521. dectab:    data.3 #001
  1522.     data.3 #002
  1523.     data.3 #004
  1524.     data.3 #008
  1525.     data.3 #016
  1526.     data.3 #032
  1527.     data.3 #064
  1528.     data.3 #128
  1529.  
  1530. ; table mapping Telmac hex key locations to HP48SX key codes
  1531. ; lsn is bit mask to output, lsn is bit mask to mask input with
  1532. keytab:
  1533.     data.2    #41
  1534.     data.2    #88
  1535.     data.2    #48
  1536.     data.2    #28
  1537.     data.2    #84
  1538.     data.2    #44
  1539.     data.2    #24
  1540.     data.2    #82
  1541.     data.2    #42
  1542.     data.2    #22
  1543.     data.2    #81
  1544.     data.2    #21
  1545.     data.2    #18
  1546.     data.2    #14
  1547.     data.2    #12
  1548.     data.2    #11
  1549.  
  1550. ; jump table for CHIP-8 instruction dispatching based on the first nibble
  1551.  
  1552. jumptab:
  1553.     data.4    i0-jtref
  1554.     data.4    i1-jtref
  1555.     data.4    i2-jtref
  1556.     data.4    i3-jtref
  1557.     data.4    i4-jtref
  1558.     data.4    i5-jtref
  1559.     data.4    i6-jtref
  1560.     data.4    i7-jtref
  1561.     data.4    i8-jtref
  1562.     data.4    i9-jtref
  1563.     data.4    ia-jtref
  1564.     data.4    ib-jtref
  1565.     data.4    ic-jtref
  1566.     data.4    id-jtref
  1567.     data.4    ie-jtref
  1568.     data.4    if-jtref
  1569.  
  1570. regsave:
  1571.     even    ; pad to even number of nibbles
  1572.  
  1573. end:    ; don't add stuff after this line.
  1574. ================================ Cut here ================================
  1575. -- 
  1576. Andreas Gustafsson
  1577. Internet: gson@niksula.hut.fi
  1578. Voice: +358 0 563 5592
  1579.  
  1580.  
  1581.